/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package r2x;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import javax.swing.JTextArea;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.collections.MultiHashMap;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

/**
 *
 * @author przemek
 */
public class XMLCreator {

    public final static String DEFAULT_DESTINATION = "out.xml";
    public final static String ILLEGAL_VALUE = "Illegal_Value";
    public final static String ILLEGAL_ELEMENT_NAME = "Illegal_element_name";
    public final static int PREFIX_LENGTH = 5;
    private DTDElement rootDTD;
    private Document dom;
    private String destination;
    private JTextArea output;

    public XMLCreator() {
        destination = DEFAULT_DESTINATION;
    }

    public XMLCreator(DTDElement _rootDTD) {
        this();
        rootDTD = _rootDTD;
    }

    void createXML() {
        createDocument();
        createDOMTree();

        new Thread() {
            public void run() {
                output.append("Started printing to file...\n");
                printToFile();
                R2XApp.getApplication().setTimes(1);
            }
        }.start();
    }

    private void createDocument() {

        //get an instance of factory
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try {
            //get an instance of builder
            DocumentBuilder db = dbf.newDocumentBuilder();

            //create an instance of DOM
            dom = db.newDocument();
        } catch (ParserConfigurationException pce) {
            //dump it
            System.out.println("Error while trying to instantiate DocumentBuilder " + pce);
            System.exit(1);
        }

    }

    private void createDOMTree() {

        //create the root element
        Element rootEle = dom.createElement(rootDTD.getName());
        dom.appendChild(rootEle);

        ListIterator<Element> it = expandDTDElement(rootDTD).listIterator();
        while (it.hasNext()) {
            rootEle.appendChild(it.next());
        }
    }

    private LinkedList<Element> expandDTDElement(DTDElement _el) {
        LinkedList<Element> resultList = new LinkedList<Element>();
        LinkedList<DTDElement> columnDTDlist = null;
        DTDElement currDTDel;
        Element currEl = null;
        String query = "", val = "", name = "";
        output.append("expanding " + _el.getName() + "\n");
        try {
            ListIterator<DTDElement> it = _el.getChildrenList().listIterator();
            while (it.hasNext()) {      //queries the database for all the tables
                currDTDel = it.next();
                if (currDTDel.getType() == DTDElement.TAB) {
                    // expanding one of tables
                    columnDTDlist = currDTDel.getChildrenList();

                    LinkedList<MultiHashMap> NNColumnsList;  //list of multimaps
                    //each of the multimaps will show
                    //the mapping of N-N relations
                    //that were put into the DTD element
                    NNColumnsList = prepareNNColumnsList(columnDTDlist, currDTDel.getName());
//                    testListOfHashMaps(NNColumnsList);

                    query = Tools.prepareSelectAllQuery(currDTDel.getName());
                    ResultSet rs = R2XApp.getApplication().getDbManager().getResultSetOfQuery(query);
                    output.append("Queried table " + currDTDel.getName() + ", " + Tools.getNumberOfRows(rs) + " records got\n");

                    // iterate through rows

/// here we are dealing with every single element:
                    while (rs.next()) {         //for each row that was obtained
                        String id = rs.getString(1);
                        int nnDone =0;
                        Element tempChild, el = dom.createElement(currDTDel.getName());
                        ListIterator<DTDElement> colIt = columnDTDlist.listIterator();

             //iterate through the columns of the dtdelement columns
                        while (colIt.hasNext()) {
                            DTDElement tempEl = colIt.next();
                            name = tempEl.getName();
                            if (tempEl.getType() == DTDElement.COL) {
                                try {
                                    val = rs.getString(tempEl.getName());
                                } catch (Exception e) {
                                    if (val == null) val = "null";
                                    val = "null";
                                }
                            tempChild = createBasicElement(name, val);
                            el.appendChild(tempChild);
                            }
                            if (tempEl.getType() == DTDElement.NNREL){
                                MultiHashMap nnHashMap = NNColumnsList.get(nnDone);
                                List occurenceList = (List) nnHashMap.get(id);
                                if (occurenceList != null){
                                ListIterator tempIt = occurenceList.listIterator();
                                while (tempIt.hasNext()){
                                  val = (String)tempIt.next();
                             tempChild = createBasicElement(name, val);
                            el.appendChild(tempChild);
                                }}
                                nnDone++;
                            }
                        }
                        resultList.add(el);
                    }
                    rs.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

//        resultList.add(createBasicElement("Nazwa wezla", "66666"));
        return resultList;
    }

    private Element createBasicElement(String _name, String _value) {
        Text n;
        Element result;
        try {
            n = dom.createTextNode(_value);
        } catch (org.w3c.dom.DOMException e) {
            output.append("Something wrong with the value " + _value + " of element " + _name + "\n");
            n = dom.createTextNode(ILLEGAL_VALUE);
        }
        try {
            result = dom.createElement(_name);
        } catch (org.w3c.dom.DOMException e) {
            output.append("Something wrong with element " + _name + "\n");
            result = dom.createElement(ILLEGAL_ELEMENT_NAME);
        }
        result.appendChild(n);
        return result;
    }

    private LinkedList<MultiHashMap> prepareNNColumnsList(LinkedList<DTDElement> columnDTDlist,
            String ancestorName) {
        LinkedList<MultiHashMap> resultList = new LinkedList<MultiHashMap>();
        DTDElement temp;
        ListIterator<DTDElement> it = columnDTDlist.listIterator();
        while (it.hasNext()){
            temp = it.next();
            if (temp.getType() == DTDElement.NNREL)
                resultList.add(createHashMapFromNNTable(temp.getName(), ancestorName));
        }
        
        return resultList;
    }

    private MultiHashMap createHashMapFromNNTable(String nnTableName,String ancestorName) {
        MultiHashMap resultHashMap = new MultiHashMap();
        String query = Tools.prepareSelectAllQuery(nnTableName);
        ResultSet rs = R2XApp.getApplication().getDbManager().getResultSetOfQuery(query);
        String keyName, valueName, col1Name, col2Name;
        output.append("Queried NN Table " + nnTableName +
                " for " + ancestorName + ", " +
                Tools.getNumberOfRows(rs) + " records got\n");
        try {
            col1Name = rs.getMetaData().getColumnName(1);
            col2Name = rs.getMetaData().getColumnName(2);

            //determining which column should be a key in hashmap
            if (col1Name.startsWith(ancestorName.substring(0, PREFIX_LENGTH))){
                keyName = col1Name;
                valueName = col2Name;
            }
            else {
            if (col2Name.startsWith(ancestorName.substring(0, PREFIX_LENGTH))){
                keyName = col2Name;
                valueName = col1Name;
            } else{
                output.append("Unable to create proper hashmap for table "
                        + nnTableName+", \nname of one of the columns should begin with"+
                        ancestorName.substring(0, PREFIX_LENGTH)+
                         "Resulting XML may be invalid!!!\n");
                                        keyName = col1Name;
                valueName = col2Name;
            }
            }
//            output.append("keyNameInRS= "+keyName+", valueNameInRS= "+valueName+"\n");

            while (rs.next()) {
                resultHashMap.put(rs.getString(keyName), rs.getString(valueName));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultHashMap;
    }

    private void printToFile() {

        try {
            //print
            OutputFormat format = new OutputFormat(dom);
            format.setIndenting(true);

            //to generate output to console use this serializer
            //XMLSerializer serializer = new XMLSerializer(System.out, format);


            //to generate a file output use fileoutputstream instead of system.out
            XMLSerializer serializer = new XMLSerializer(
                    new FileOutputStream(new File(getDestination())), format);

            serializer.serialize(dom);
            serializer = null;
            System.gc();
            output.append("End of writing generated XML to " + getDestination() + 
                    "\n_______________________________________________________\n\n\n");

        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }

    /**
     * @return the root
     */
    public DTDElement getRoot() {
        return rootDTD;
    }

    /**
     * @param root the root to set
     */
    public void setRoot(DTDElement root) {
        this.rootDTD = root;
    }

    /**
     * @return the output
     */
    public JTextArea getOutput() {
        return output;
    }

    /**
     * @param output the output to set
     */
    public void setOutput(JTextArea output) {
        this.output = output;
    }

    private void testListOfHashMaps(LinkedList<MultiHashMap> NNColumnsList) {
        if (NNColumnsList.size() == 0){
            output.append("   * No hashMap");
        }
        ListIterator it = NNColumnsList.listIterator();
        while (it.hasNext()){
            MultiHashMap testMap = (MultiHashMap) it.next();
            output.append("   * Tested hashMap has size: " + testMap.size()+"\n");
        }
    }

    /**
     * @return the destination
     */
    public String getDestination() {
        return destination;
    }

    /**
     * @param destination the destination to set
     */
    public void setDestination(String destination) {
        this.destination = destination;
    }
}
